/**
 * Copyright (C) 2009 Ignasi Fosch Alonso
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version.
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 
 * details.
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 * Place, Suite 330, Boston, MA 02111-1307 USA
 *
 * You can reach me by mail at natx@y10k.ws
 * You can reach him by paper mail at:
 * C. LLEIDA, 41-43, 3r 1a
 * El Prat de Llobregat 08820
 * Barcelona, SPAIN
 */
package ws.y10k.vi;

import java.net.MalformedURLException;
import java.net.URL;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Calendar;

import ws.y10k.vsmanager.VSManager;

import com.vmware.vim25.HostSystemPowerState;
import com.vmware.vim25.InvalidProperty;
import com.vmware.vim25.RuntimeFault;
import com.vmware.vim25.mo.Datastore;
import com.vmware.vim25.mo.HostSystem;
import com.vmware.vim25.mo.Network;
import com.vmware.vim25.mo.ServiceInstance;
import com.vmware.vim25.mo.VirtualMachine;

/**
 * @author natx
 *
 */
public class Host implements TreeLeaf{

	HostSystem hs;
	private HostTable parent;
	
	private String username;
	private String password;
	private HostProtocol hostProtocol;
	private String FQDN;
	private String UUID;
	private String MIP;
	private String softwareFullName;
	private String API;
	private String license;
	private String OSType;
	private String BIOSInfo;
	private String computer;
	private String CPU;
	private long CPUMax = 0;
	private String memory;
	private long memoryMax = 0;
	private ArrayList<TreeLeaf> children;

	public boolean isUnknown() {
		return this.hs.getRuntime().getPowerState() == HostSystemPowerState.unknown;
	}

	public boolean isStandby() {
		return this.hs.getRuntime().getPowerState() == HostSystemPowerState.standBy;
	}

	public boolean isInMaintenanceMode() {
		return this.hs.getRuntime().inMaintenanceMode;
	}
	
	private void addChild(TreeLeaf child) {
		children.add(child);
	}

	public boolean equals(Object obj1) {
		boolean isEqual = false;
		
		if ( obj1 instanceof Host ) {
			if ( ((Host) obj1).getUUID().equals(this.getUUID()) ) {
				isEqual = true;
			}
		}
		
		return isEqual;
	}
	
	public void connect() throws RemoteException, MalformedURLException, HostDataEmptyException, VIVersionNotSupported {
		this.connect(this.FQDN, this.username, this.password, this.hostProtocol);
	}

	private String getUUID() {
		return this.UUID;
	}

	public String getFQDN() {
		return this.FQDN;
	}

	public String getMIP() {
		return this.MIP;
	}
	
	public void connect(String hostname, String username, String password, HostProtocol protocol) throws HostDataEmptyException, RemoteException, MalformedURLException, VIVersionNotSupported {
		// TODO i18n
		if ( hostname.isEmpty() || username.isEmpty() || password.isEmpty() )
			throw new HostDataEmptyException();
		String url = protocol.protocol() + "://" + hostname + "/sdk/vimService";
		ServiceInstance si = new ServiceInstance(new URL(url), username, password, true);

		if ( si.getAboutInfo().getName().startsWith("VMware ESXi") || si.getAboutInfo().getName().startsWith("VMware ESX") ) {
			this.username = username;
			this.password = password;
			this.hostProtocol = protocol;
			this.hs = ESX.getHostSystem(si);
			if ( this.FQDN == null ) this.FQDN = this.hs.getName();
			if ( this.UUID == null ) this.UUID = this.hs.getHardware().getSystemInfo().getUuid();
			if ( this.MIP == null ) this.MIP = this.hs.getSummary().getManagementServerIp();
			if ( this.softwareFullName == null ) this.softwareFullName = this.hs.getConfig().getProduct().getFullName();
			if ( this.API == null ) this.API = this.hs.getConfig().getProduct().getApiType() + " " + this.hs.getConfig().getProduct().getApiVersion();
			if ( this.license == null ) this.license = this.hs.getConfig().getProduct().getLicenseProductName() + " " + this.hs.getConfig().getProduct().getLicenseProductVersion();
			if ( this.OSType == null ) this.OSType = this.hs.getConfig().getProduct().getOsType();
			if ( this.BIOSInfo == null ) this.BIOSInfo = this.hs.getHardware().getBiosInfo().getBiosVersion() + " " + this.hs.getHardware().getBiosInfo().getReleaseDate().get(Calendar.DATE) + "/" + this.hs.getHardware().getBiosInfo().getReleaseDate().get(Calendar.MONTH) + "/" + this.hs.getHardware().getBiosInfo().getReleaseDate().get(Calendar.YEAR);
			if ( this.computer == null ) this.computer = this.hs.getHardware().getSystemInfo().getVendor() + " " + this.hs.getHardware().getSystemInfo().getModel();
			if ( this.CPU == null ) this.CPU = this.hs.getHardware().getCpuInfo().getNumCpuCores()+ "x" + (this.hs.getHardware().getCpuInfo().getHz() / 1000000000) + "GHz";
			if ( this.CPUMax == 0 ) this.CPUMax = this.hs.getHardware().getCpuInfo().getNumCpuCores() * this.hs.getHardware().getCpuInfo().getHz() / 1000000;
			if ( this.memory == null ) this.memory = (this.hs.getHardware().getMemorySize() / 1048576) + "MB";
			if ( this.memoryMax == 0 ) this.memoryMax = (this.hs.getHardware().getMemorySize() / 1048576);
			VirtualMachine[] vms = hs.getVms();
			for ( int i = 0; i < vms.length; i++ ) {
				Guest guest = new Guest(vms[i], this);
				if ( !children.contains(guest) ) {
					VSManager.save(guest);
					addChild(guest);
				}
			}
		} else {
			throw new VIVersionNotSupported(si.getAboutInfo().getName());
		}
	}

	public void setParent(HostTable hostConnectionTable) {
		this.parent = hostConnectionTable;		
	}
	
	public TreeLeaf getParent() {
		return (TreeLeaf)parent;
	}

	@Override
	public Object[] getChildren() throws InvalidProperty, RuntimeFault, RemoteException {
		return children.toArray();
	}

	@Override
	public boolean hasChildren() {
		return !children.isEmpty();
	}

	@Override
	public void setParent(TreeLeaf parent) {
		parent = (HostTable)parent;
	}

	@Override
	public String getName() {
		return this.getFQDN();
	}

	public String getSoftware() {
		return this.softwareFullName;
	}

	public String getAPI() {
		return this.API;
	}
	
	public String getLicense() {
		return this.license;
	}

	public String getOSType() {
		return this.OSType;
	}

	public String getBIOSInfo() {
		return this.BIOSInfo;
	}

	public String getComputer() {
		return this.computer;
	}

	public String getCPU() {
		return this.CPU;
	}

	public Integer getCPUMax() {
		return (int)this.CPUMax;
	}

	public Integer getCPUUsage() {
		return this.hs.getSummary().getQuickStats().getOverallCpuUsage();
	}

	public String getMemory() {
		return this.memory;
	}

	public Integer getMemoryMax() {
		return (int)this.memoryMax;
	}

	public Integer getMemoryUsage() {
		return this.hs.getSummary().getQuickStats().getOverallMemoryUsage();
	}
	
	public String[] getDataStoreNames() throws InvalidProperty, RuntimeFault, RemoteException {
		Datastore[] datastores = this.hs.getDatastores();
		String[] datastoreNames = new String[datastores.length];
		for ( int i = 0; i < datastores.length; i++ ) {
			datastoreNames[i] = datastores[i].getName();
		}
		return datastoreNames;
	}
	
	public String[] getNetworkNames() throws InvalidProperty, RuntimeFault, RemoteException {
		Network[] nets = this.hs.getNetworks();
		String[] networkNames = new String[nets.length];
		for ( int i = 0; i < nets.length; i++ ) {
			networkNames[i] = nets[i].getName();
		}
		return networkNames;
	}
	
	public int getNetworkVmsCount(String network) throws InvalidProperty, RuntimeFault, RemoteException, NetworkNotFoundException {
		Network[] nets = this.hs.getNetworks();
		for ( int i = 0; i < nets.length; i++ ) {
			if ( nets[i].getName().equals(network) ) return nets[i].getVms().length;
		}
		throw new NetworkNotFoundException();
	}

	public long getDataStoreSize(String datastore) throws InvalidProperty, RuntimeFault, RemoteException, DatastoreNotFoundException {
		Datastore[] datastores = this.hs.getDatastores();
		for ( int i = 0; i < datastores.length; i++ ) {
			if ( datastores[i].getName().equals(datastore) ) return datastores[i].getSummary().getCapacity() / 1073741824;
		}
		throw new DatastoreNotFoundException();
	}

	public long getDataStoreUsed(String datastore) throws InvalidProperty, RuntimeFault, RemoteException, DatastoreNotFoundException {
		Datastore[] datastores = this.hs.getDatastores();
		for ( int i = 0; i < datastores.length; i++ ) {
			if ( datastores[i].getName().equals(datastore) ) return (datastores[i].getSummary().getCapacity() - datastores[i].getSummary().getFreeSpace()) / 1073741824;
		}
		throw new DatastoreNotFoundException();
	}
	
	public Host(String hostname, String username, String password) throws RemoteException, MalformedURLException, HostDataEmptyException, VIVersionNotSupported {
		this(hostname, username, password, HostProtocol.HTTPS);
	}
	
	public Host(String hostname, String username, String password, HostProtocol protocol) throws RemoteException, MalformedURLException, HostDataEmptyException, VIVersionNotSupported {
		children = new ArrayList<TreeLeaf>();
		if ( hostname != null && username != null && password != null ) {
			connect(hostname, username, password, protocol);
		}
	}
	
}
